#include "wl_timer.h"

#include "wl_list.h"
#include "wl_malloc.h"
#include "wl_mutex.h"
#include "wl_timer_inner.h"
#include "wl_trace.h"
#include "wl_utils.h"

#include <pthread.h>
#include <sys/prctl.h>
#include <time.h>
#include <unistd.h>
#include <ak_thread.h>

#define WL_TIMER_NAME_LEN 40

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

typedef struct __wl_timer_message {
    WL_TIMER timer;
} wl_timer_message;

typedef struct {
    wl_uint32     timerid;
    wl_int32      taskid;
    WL_TIMER_TYPE type;
    wl_uint32     interval;
    wl_int32      lifecycle;  //生命期 小于等于0响应
    wl_char       name[WL_TIMER_NAME_LEN];
    wl_timer_callback callback;
    void*             context;
} wl_timer_info;

typedef struct {
    wl_bool  initialized;
    wl_list  timer_list;
    WL_MUTEX mutex;
    wl_bool  exit;
    ak_pthread_t thread;
} wl_timer_global;

/*-------------------------------------------------------------------------
模块内部全局变量
-------------------------------------------------------------------------*/
static wl_timer_global s_timer_global;

void timer_clock(wl_int32 clock) {
    wl_list_iterator iterator;
    wl_timer_info* timer_info = (wl_timer_info*)wl_list_get_first(&s_timer_global.timer_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (timer_info->lifecycle > 0) {
            timer_info->lifecycle -= clock;
            if (0 >= timer_info->lifecycle) {
                wl_task_message message;
                wl_timer_message timer_message;
                memset(&message, 0, sizeof(wl_task_message));
                timer_message.timer = timer_info;
                message.type = WL_TASK_MSG_TIMER;
                memcpy(&message.data, &timer_message, sizeof(wl_timer_message));
                message.datalen = sizeof(wl_timer_message);
                wl_send_task_message(timer_info->taskid, &message);
                if (REPETITIVE == timer_info->type) {
                    timer_info->lifecycle = timer_info->interval;
                }
            }
        }
        timer_info = (wl_timer_info*)wl_list_get_next(&iterator);
    }
}

void* timer_main_thread(void* param) {
    UNUSED(param);
    WLLOGT("timer start");

    prctl(PR_SET_NAME, "timer_main_thread");

    while (!s_timer_global.exit) {
        wl_mutex_acquire(&s_timer_global.mutex);
        timer_clock(100);
        wl_mutex_release(&s_timer_global.mutex);
        wl_msleep(100);
    }

    ak_thread_exit();
    WLLOGT("timer exit");
    return wl_null;
}

wl_int32 wl_timer_init(void) {
    WLLOGW("wl_timer_init");
    if (!s_timer_global.initialized) {
        if (0 != wl_mutex_create("wlsdk_timer", &s_timer_global.mutex)) {
            WLLOGW("wl_mutex_create fail");
            return -1;
        }
        wl_list_init(&s_timer_global.timer_list);
        s_timer_global.exit = wl_false;
        if (0 != ak_thread_create(&s_timer_global.thread, timer_main_thread, wl_null, ANYKA_THREAD_MIN_STACK_SIZE, -1)) {
            wl_mutex_delete(&s_timer_global.mutex);
            WLLOGW("pthread_create fail");
            return -1;
        }

        WLLOGW("wl_timer_init success");
        s_timer_global.initialized = wl_true;
    }
    return 0;
}

void wl_timer_end(void) {
    if (s_timer_global.initialized) {
        s_timer_global.exit = wl_true;
        ak_thread_join(s_timer_global.thread);
        wl_mutex_acquire(&s_timer_global.mutex);
        wl_list_free(&s_timer_global.timer_list);
        wl_mutex_release(&s_timer_global.mutex);
        wl_mutex_delete(&s_timer_global.mutex);
        s_timer_global.initialized = wl_false;
    }
}

wl_int32 wl_timer_process_message(wl_task_message* message) {
    wl_timer_message  timer_message;
    wl_list_iterator  iterator;
    wl_timer_info*    timer_info = wl_null;
    wl_timer_callback callback   = wl_null;
    void*             context    = wl_null;
    if (wl_null == message || sizeof(wl_timer_message) != message->datalen) {
        WLLOGW("message is null");
        return -1;
    }
    memcpy(&timer_message, &(message->data), message->datalen);
    wl_mutex_acquire(&s_timer_global.mutex);
    timer_info = (wl_timer_info*)wl_list_get_first(&s_timer_global.timer_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (timer_message.timer == timer_info) {
            callback = timer_info->callback;
            context  = timer_info->context;
            //定时器释放
            if (0 >= timer_info->lifecycle) {
                wl_free(timer_info);
                wl_list_iterator_remove(&iterator);
            }
            break;
        }
        timer_info = (wl_timer_info*)wl_list_get_next(&iterator);
    }
    wl_mutex_release(&s_timer_global.mutex);

    if (callback) {
        callback(timer_message.timer, context);
    }
    return 0;
}

wl_int32 wl_timer_create_timerid(void) {
    wl_list_iterator iterator;
    wl_uint32        timerid    = 1;
    wl_uint32        maxtimerid = 1;
    wl_timer_info*   timer_info = (wl_timer_info*)wl_list_get_first(&s_timer_global.timer_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (timer_info->timerid > maxtimerid) {
            maxtimerid = timer_info->timerid;
        }
        // TIMERID已经分配
        if (timer_info->timerid == timerid) {
            timerid = maxtimerid + 1;
        }
        timer_info = (wl_timer_info*)wl_list_get_next(&iterator);
    }
    return timerid;
}

WL_TIMER
wl_timer_start(wl_uint32         interval,
               WL_TIMER_TYPE     type,
               wl_timer_callback callback,
               void*             context,
               wl_int32          taskid,
               wl_char*          timer_name) {
    wl_timer_info* timer_info;

    wl_mutex_acquire(&s_timer_global.mutex);
    timer_info = (wl_timer_info*)wl_malloc(sizeof(wl_timer_info));
    if (wl_null != timer_info) {
        memset(timer_info, 0, sizeof(wl_timer_info));
        timer_info->timerid   = wl_timer_create_timerid();
        timer_info->taskid    = taskid;
        timer_info->type      = type;
        timer_info->interval  = interval;
        timer_info->lifecycle = interval;
        timer_info->callback  = callback;
        timer_info->context   = context;
        if (timer_name) {
            strncpy(timer_info->name, timer_name, sizeof(timer_info->name) - 1);
            timer_info->name[sizeof(timer_info->name) - 1] = '\0';
        }
        WLLOGT("wl_timer_start timer %s", timer_info->name);
        if (-1 == wl_list_add(&s_timer_global.timer_list, timer_info, -1)) {
            WLLOGW("wl_list_add fail");
            wl_free(timer_info);
            wl_mutex_release(&s_timer_global.mutex);
            return wl_null;
        }
    }
    wl_mutex_release(&s_timer_global.mutex);
    WLLOGT("wl_timer_start success");
    return (WL_TIMER)timer_info;
}

wl_int32 wl_timer_stop(WL_TIMER timer) {
    wl_timer_info*   timer_info;
    wl_list_iterator iterator;

    wl_mutex_acquire(&s_timer_global.mutex);
    timer_info = (wl_timer_info*)wl_list_get_first(&s_timer_global.timer_list, &iterator);
    while (!wl_list_iterator_is_end(&iterator)) {
        if (timer == timer_info) {
            wl_free(timer_info);
            wl_list_iterator_remove(&iterator);
            break;
        }
        timer_info = (wl_timer_info*)wl_list_get_next(&iterator);
    }
    wl_mutex_release(&s_timer_global.mutex);
    WLLOGT("wl_timer_stop end");
    return 0;
}

#ifdef __cplusplus
}
#endif /* __cplusplus */
